package com.hmall.gateway.routes;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final NacosConfigManager nacosConfigManager;

    private final RouteDefinitionWriter routeDefinitionWriter;

    private final String dataId = "gateway-routes.json";

    private final String group = "DEFAULT_GROUP";

    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    @PostConstruct
    public void initRouteConfigListener() throws NacosException {
        // 1、 项目启动时，先拉取一次配置，并且添加配置监听器
        String configAndSignListener = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000,
                new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String s) {
                        // 2、 监听到配置变更，需要更新路由表
                        updateRouteConfig(s);
                    }
                }
        );
        // 3、 第一次读取到配置，也需要更新路由表
        updateRouteConfig(configAndSignListener);
    }

    public void updateRouteConfig(String config) {
        // 1、 解析配置信息，转为RouteDefinition
        List<RouteDefinition> routeDefinitionList = JSONUtil.toList(config, RouteDefinition.class);
        // 2、 更新前先清空旧路由
        for (String routeId : routeIds) {
            routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
        // 3、 判断是否有新路由更新
        if (CollUtil.isEmpty(routeDefinitionList)) {
            // 无更新路由，直接结束
            return;
        }

        // 4、 更新路由
        routeDefinitionList.forEach(routeDefinition -> {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            // 记录路由id，方便将来删除
            routeIds.add(routeDefinition.getId());
        });

    }
}
